//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>
#include "lcdctrl.h"

const uint_t c_uLCDPixelsPerRow                 = 240;
const uint_t c_uLCDPixelsPerColumn              = 320;
const uint_t c_uLCDBitsPerPixel                 = 16;

const physaddr_t c_paLCDFrameBufferStartAddr    = 0xcd000000;
const uint_t c_uLCDFrameBufferSize              =
        c_uLCDPixelsPerRow * c_uLCDPixelsPerColumn * c_uLCDBitsPerPixel / 8;

//
// Lcd
//
class Lcd : public CDeviceDriver {
public:
    CARAPI Read(
        /* [in] */ Int64 u64Offset,
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf & buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf & inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    virtual void Dispose();

public:
    ECode Initialize(Address physbase);

private:
    Address     m_physbase;
    Address     m_virtbase;
};



ECode Lcd::Initialize(Address physbase)
{
    m_physbase = physbase;
    m_virtbase = 0;

	return NOERROR;
}

ECode Lcd::Read(
    /* [in] */ Int64 u64Offset,
    /* [in] */ Int32 bytesToRead,
    /* [out] */ MemoryBuf * pBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode Lcd::Write(
    /* [in] */ Int64 u64Offset,
    /* [in] */ const MemoryBuf & buffer,
    /* [out] */ Int32 * pBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode Lcd::Control(
    /* [in] */ Handle32 nControlCode,
    /* [in] */ const MemoryBuf & inBuffer,
    /* [out] */ MemoryBuf * pOutBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    ECode ec;
    LcdInfo *pInfo;

    switch (nControlCode) {
    case LcdControl_Open:
        ec = DzIoRemap(
            0,
            RoundUp2(c_uLCDFrameBufferSize, PAGE_SIZE),
            m_physbase,
            0,
            MemoryProtection_RWIO,
            &m_virtbase);
        if (FAILED(ec)) {
            m_virtbase = 0;
            return ec;
        }

        assert(0 != m_virtbase);
        kprintf("Mapped 0x%x to 0x%x, size: 0x%x\n",
                m_physbase, m_virtbase,
                RoundUp2(c_uLCDFrameBufferSize, PAGE_SIZE));

        break;


    case LcdControl_GetInfo:
        if (pOutBuffer == NULL || !pOutBuffer->GetCapacity()
            || sizeof(LcdInfo) > (size_t)pOutBuffer->GetCapacity()) {
            return E_INVALID_ARGUMENT;
        }

        pInfo = (LcdInfo *)pOutBuffer->GetPayload();
        pInfo->FrameBufferStartAddr = m_virtbase;
        pInfo->PixelsPerRow         = c_uLCDPixelsPerRow;
        pInfo->PixelsPerColumn      = c_uLCDPixelsPerColumn;
        pInfo->BitsPerPixel         = c_uLCDBitsPerPixel;
        pOutBuffer->SetUsed(sizeof(LcdInfo));

        break;

    default:
        return E_INVALID_OPTIONS;
    }

    return NOERROR;
}

void Lcd::Dispose()
{
    delete this;
}

EXTERN IDeviceDriver * CDECL CreateLcd(uint_t uDeviceNo, void *pvParameter)
{
    Lcd *pLcd = new Lcd();
    if (NULL == pLcd) return NULL;

    ECode ec = pLcd->Initialize(c_paLCDFrameBufferStartAddr);
    if (FAILED(ec)) {
        delete pLcd;
        return NULL;
    }

    pLcd->AddRef();
    return pLcd;
}
